package bma.common.thrift;

import java.lang.reflect.Method;
import java.lang.reflect.TypeVariable;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.thrift.TException;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TMessage;
import org.apache.thrift.protocol.TProtocol;

public class TProcessorRouter extends TProcessorModule {

	final org.slf4j.Logger log = org.slf4j.LoggerFactory
			.getLogger(TProcessorRouter.class);

	private Map<String, TProcessor> nameProcessors = new TreeMap<String, TProcessor>();

	public TProcessorRouter() {
		super();
	}

	public TProcessorRouter(Map<String, TProcessor> processors) {
		super(processors);
	}

	@Override
	public void setProcessors(Map<String, TProcessor> processors) {
		super.setProcessors(processors);
		if (processors != null) {
			for (TProcessor p : processors.values()) {
				addRouter(p);
			}
		}
	}

	public void addRouter(TProcessor p) {
		Class cls = queryInterface(p);
		Method[] ms = cls.getDeclaredMethods();
		Set<String> nlist = new HashSet<String>(ms.length);
		for (int i = 0; i < ms.length; i++) {
			nlist.add(ms[i].getName());
		}
		for (String n : nlist) {
			if (this.nameProcessors.containsKey(n)) {
				throw new IllegalArgumentException(cls + " method [" + n
						+ "] alread exists");
			}
			this.nameProcessors.put(n, p);
		}
	}

	public static Class queryInterface(TProcessor p) {
		TProcessor cp = p;
		if (p instanceof TProcessorWrapInterface) {
			TProcessorWrapInterface wi = (TProcessorWrapInterface) p;
			cp = wi.getInnerProcessor();
		}
		TypeVariable tv = cp.getClass().getTypeParameters()[0];
		return (Class) tv.getBounds()[0];
	}

	@Override
	public TProcessor dispatch(TMessage msg, TProtocol in, TProtocol out)
			throws TException {
		if (this.nameProcessors.containsKey(msg.name)) {
			TProcessor p = this.nameProcessors.get(msg.name);
			if (log.isDebugEnabled()) {
				log.debug("{} => {}", msg.name, p);
			}
			return p;
		}
		return super.dispatch(msg, in, out);
	}

}
